package jmine.tec.di.wrapper;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;

import jmine.tec.utils.Tuple;
import jmine.tec.utils.UnmodifiableTuple;
import jmine.tec.utils.collection.impl.ClassHierarchyMap;

/**
 * @author takeshi.fukushima
 */
public final class Wrappers {

    public static final Wrapper SORTED_SET_WRAPPER = new SortedSetWrapper();

    public static final Wrapper NO_WRAPPER = new NoWrapper();

    public static final Wrapper SET_WRAPPER = new SetWrapper();

    public static final Wrapper LIST_WRAPPER = new ListWrapper();

    public static final Wrapper MAP_WRAPPER = new MapWrapper();

    public static final Wrapper SORTED_MAP_WRAPPER = new SortedMapWrapper();

    public static final ArrayWrapper ARRAY_WRAPPER = new ArrayWrapper();

    /**
     */
    private Wrappers() {
    }

    private static final Map<Class<?>, Wrapper> WRAPPERS = new ClassHierarchyMap<Wrapper>();

    static {
        WRAPPERS.put(Object.class, NO_WRAPPER);
        WRAPPERS.put(Set.class, SET_WRAPPER);
        WRAPPERS.put(SortedSet.class, SORTED_SET_WRAPPER);
        WRAPPERS.put(List.class, LIST_WRAPPER);
        WRAPPERS.put(Map.class, MAP_WRAPPER);
        WRAPPERS.put(SortedMap.class, SORTED_MAP_WRAPPER);
        WRAPPERS.put(Tuple.class, new AbstractWrapper() {

            private static final long serialVersionUID = 6216840255688281729L;

            public boolean isTypeCompatible(Class<?> containerType) {
                return Tuple.class.isAssignableFrom(containerType);
                }

            public Object wrap(Object[] propertyKeys, Object[] propertyValues, Class<?> valueType) {
                return new UnmodifiableTuple<Object, Object>(propertyKeys[0], propertyValues[0]);
                }

            @Override
            public Object wrap(Object propertyKey, Object propertyValues, Class<?> valueType) {
                return new UnmodifiableTuple<Object, Object>(propertyKey, propertyValues);
                }

            public boolean checkContainedType(Object resolved, Class<?> requiredComponentClass) {
                Tuple<?, ?> anyTuple = (Tuple<?, ?>) resolved;
                return requiredComponentClass.isInstance(anyTuple.tail());
            }

            public List<Tuple<?, ?>> collect(Object resolved, String name) {
                Tuple<?, ?> anyTuple = (Tuple<?, ?>) resolved;
                return Collections.<Tuple<?, ?>> singletonList(anyTuple);
            }

        });
    }

    /**
     * Procura um 'wrapper' para um determinado tipo
     * 
     * @param targetType o tipo
     * @return o {@link Wrapper}
     */
    public static Wrapper findWrapperFor(Class<?> targetType) {
        if (targetType.isArray()) {
            return ARRAY_WRAPPER;
        }
        final Wrapper wrapper = WRAPPERS.get(targetType);
        if (wrapper == null) {
            return WRAPPERS.get(Object.class);
        }
        return wrapper;
    }
}
